﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;

namespace GFluentCode.Tools
{
    /// <summary>
    /// 
    /// </summary>
    public static class EnumerableExtension
    {
        #region 查询元素所在位置

        /// <summary>
        /// 搜索与指定的条件匹配的元素，然后返回枚举器首个匹配项从零开始的索引。
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="collection"></param>
        /// <param name="search"></param>
        /// <returns></returns>
        public static int GFindIndex<T>(this IEnumerable<T> collection,T search)
        {
            if (typeof(T) == typeof(string))
            {
                var txt = (search == null) ? "" : search.ToString();
                return collection.GFindIndex(delegate (T y)
                {
                    if (y == null) return string.IsNullOrWhiteSpace(txt);

                    return string.Equals(txt, y.ToString(), StringComparison.OrdinalIgnoreCase);
                });                
            }

            return collection.GFindIndex(delegate (T y)
            {
                if (y == null) return search == null;

                return y.Equals(search);
            });
        }

        /// <summary>
        /// 查询元素所在位置
        /// </summary>        
        public static int GFindIndex<T>(this IEnumerable<T> collection, Predicate<T> match)
        {
            if (collection == null || match == null) return -1;

            int index = 0;
            foreach (T cur in collection)
            {
                if (match(cur)) return index;

                index++;
            }

            return -1;
        }

        /// <summary>
        /// 查询元素所在位置
        /// </summary>        
        public static int GFindIndex<T>(this IEnumerable collection, Predicate<T> match)
        {
            if (collection == null || match == null) return -1;

            int num = 0;

            IEnumerator enumerator = collection.GetEnumerator();

            try
            {
                while (enumerator.MoveNext())
                {
                    T obj = (T)((object)enumerator.Current);
                    if (match(obj)) return num;
                    num++;
                }
            }
            finally
            {
                var disposable = enumerator as IDisposable;
                if (disposable != null) disposable.Dispose();
            }

            return -1;
        }



        /// <summary>
        /// 查询元素所在位置
        /// </summary>        
        public static int GFindIndex<T>(this IList<T> collection, Predicate<T> match)
        {
            if (collection == null || match == null) return -1;

            int index = 0;
            foreach (T cur in collection)
            {
                if (match(cur)) return index;

                index++;
            }

            return -1;
        }

        #endregion

        #region 判断存在 

        /// <summary>
        /// 判断存在
        /// </summary>        
        public static bool GExists<T>(this IEnumerable<T> values, Predicate<T> match)
        {
            return values.GFindIndex(match) >= 0;
        }

        /// <summary>
        /// 确定指定枚举器总的元素是否与指定谓词定义的条件匹配。
        /// </summary>        
        public static bool GExists<T>(this IEnumerable values, Predicate<T> match)
        {
            return values.GFindIndex(match) >= 0;
        }

        /// <summary>
        /// 确定在枚举器中的元素是否与指定谓词定义的条件匹配。
        /// </summary>
        /// <param name="values">用于判断的当前枚举器</param>
        /// <param name="search">如果为字符串忽略大小写。</param>        
        public static bool GExists<T>(this IEnumerable<T> values, T search)
        {
            return values.GFindIndex(search) >= 0;
        }
        #endregion

        #region 查询单个

        /// <summary>
        /// 查询符合条件的第一个
        /// </summary>
        public static T GFind<T>(this IList<T> collection, Predicate<T> match)
        {
            if (collection == null || match == null) return default(T);

            foreach (T cur in collection)
            {
                if (match(cur)) return cur;
            }

            return default(T);
        }

        /// <summary>
        /// 查询符合条件的第一个
        /// </summary>
        public static T GFind<T>(this IEnumerable<T> collection, Predicate<T> match)
        {
            return new List<T>(collection).GFind(match);
        }

        /// <summary>
        /// Array符合条件的第一个
        /// </summary>
        public static T GFind<T>(this T[] collection, Predicate<T> match)
        {
            return collection.ToList().GFind(match);
        }
        #endregion

        #region 查询所有

        /// <summary>
        /// 查询所有
        /// </summary>
        public static IList<T> GFindAll<T>(this IList<T> collection, Predicate<T> match)
        {
            if (collection == null || match == null) return collection;

            var list = new List<T>();

            foreach (T cur in collection)
            {
                if (match(cur)) list.Add(cur);
            }

            return list;
        }

        /// <summary>
        /// 查询所有
        /// </summary>
        public static IList<T> GFindAll<T>(this IEnumerable<T> collection, Predicate<T> match)
        {
            return new List<T>(collection).GFindAll(match);
        }

        /// <summary>
        /// 查询所有
        /// </summary>
        public static T[] GFindAll<T>(this T[] collection, Predicate<T> match)
        {
            return collection.ToList().GFindAll(match).ToArray();
        }

        /// <summary>
        /// 递归查询
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="Tid"></typeparam>
        /// <param name="collection"></param>
        /// <param name="id"></param>
        /// <param name="idSelector"></param>
        /// <param name="parentSelector"></param>
        /// <param name="direction"></param>
        /// <param name="includeSelf"></param>
        /// <returns></returns>
        public static IList<T> GFindAll<T, Tid>(this IEnumerable<T> collection, Tid id,
            Func<T, Tid> idSelector, Func<T, Tid> parentSelector
            , RecursionDirection direction = RecursionDirection.Children, bool includeSelf = false) where T : class
        {
            if (collection == null) return new List<T> { default(T) };

            if (idSelector == null || parentSelector == null) throw new Exception("No IdSelector or ParentSelector");

            if (direction == RecursionDirection.Parent)
            {
                T t = collection.GFind((T x) => idSelector(x).Equals(id));
                if (t == null) return new List<T> { default(T) };

                IList<T> arr = collection.GFindAll(parentSelector(t), idSelector, parentSelector, RecursionDirection.Parent, true);
                if (includeSelf) arr.Add(t);

                return arr;
            }
            else
            {
                if (direction == RecursionDirection.Children)
                {                    
                    List<T> list = new List<T>();
                    if (includeSelf)
                    {
                        T t2 = collection.GFind((T x) => idSelector(x).Equals(id));
                        if (t2 != null) list.Add(t2);                        
                    }
                    var arr2 = collection.GFindAll((T x) => parentSelector(x).Equals(id));
                    for (var i = 0; i < arr2.Count; i++)
                    {
                        T t3 = arr2[i];
                        list.Add(t3);
                        list.AddRange(collection.GFindAll(idSelector(t3), idSelector, parentSelector, RecursionDirection.Children, false));
                    }
                    return list;
                }

                IList<T> tmp = collection.GFindAll(id, idSelector, parentSelector, RecursionDirection.Parent, true);
                IList<T> tmp2 = collection.GFindAll(id, idSelector, parentSelector, RecursionDirection.Children, false);
                tmp.ToList().AddRange(tmp2);
                return tmp;
            }
        }
        #endregion

        #region 去除某元素
        /// <summary>
        /// 去除某元素
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="collection"></param>
        /// <param name="match"></param>
        /// <returns></returns>
        public static IList<T> GExclude<T>(this IList<T> collection, Predicate<T> match)
        {
            if (collection == null || match == null) return collection;

            for (var i = collection.Count - 1; i >= 0; i--)
            {
                if (match(collection[i]))
                    collection.RemoveAt(i);
            }

            return collection;
        }

        /// <summary>
        /// 去除某元素
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="collection"></param>
        /// <param name="match"></param>
        /// <returns></returns>
        public static IList<T> GExclude<T>(this IEnumerable<T> collection, Predicate<T> match)
        {
            return new List<T>(collection).GExclude(match);
        }

        /// <summary>
        /// 去除某元素
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="collection"></param>
        /// <param name="match"></param>
        /// <returns></returns>
        public static IList<T> GExclude<T>(this T[] collection, Predicate<T> match)
        {
            return new List<T>(collection).GExclude(match);
        }

        /// <summary>
        /// 去除某元素
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="collection"></param>
        /// <param name="match"></param>
        /// <returns></returns>
        public static T[] GExclude2<T>(this T[] collection, Predicate<T> match)
        {
            return new List<T>(collection).GExclude(match).ToArray();
        }
        #endregion

        #region 转List

        /// <summary>
        /// 转List
        /// </summary>
        public static IList<Tvalue> GToList<Titem, Tvalue>(this IList<Titem> collection, Func<Titem, Tvalue> selector, bool removeEmpty = false)
        {
            if (collection == null || selector == null) return new List<Tvalue> { default(Tvalue) };

            List<Tvalue> list = new List<Tvalue>();

            if (typeof(Tvalue) == typeof(string))
            {
                using (IEnumerator<Titem> enumerator = collection.GetEnumerator())
                {
                    while (enumerator.MoveNext())
                    {
                        Titem cur = enumerator.Current;
                        Tvalue val = selector(cur);
                        if (!removeEmpty || !string.IsNullOrWhiteSpace((string)Convert.ChangeType(val, typeof(string))))
                            list.Add(val);
                    }
                    return list;
                }
            }

            foreach (Titem cur in collection)
            {
                Tvalue val = selector(cur);
                if (!removeEmpty || val != null) list.Add(val);
            }

            return list;
        }

        /// <summary>
        /// 转List
        /// </summary>
        public static IList<Tvalue> GToList<Titem, Tvalue>(this IEnumerable<Titem> collection, Func<Titem, Tvalue> selector, bool removeEmpty = false)
        {
            return new List<Titem>(collection).GToList(selector, removeEmpty);
        }
        #endregion

        #region 转数组

        /// <summary>
        /// 转数组
        /// </summary>        
        public static Tvalue[] GToArray<Titem, Tvalue>(this IList<Titem> collection, Func<Titem, Tvalue> selector, bool removeEmpty = false)
        {
            return collection.GToList(selector, removeEmpty).ToArray();
        }
        #endregion

        #region 二维转一维

        /// <summary>
        /// 二维数组转一维数组
        /// </summary>        
        public static T[] GToFloor<T, Tenumer>(this IEnumerable<Tenumer> collection, Func<Tenumer, IEnumerable<T>> selector)
        {
            if (collection == null || selector == null) return new T[0];

            List<T> list = new List<T>();

            foreach (Tenumer cur in collection)
            {
                IEnumerable<T> enumerable = selector(cur);
                if (enumerable != null) list.AddRange(enumerable);
            }

            return list.ToArray();
        }

        /// <summary>
        /// 二维转一维
        /// </summary>
        public static T[] GToFloor<T>(this IEnumerable<IEnumerable<T>> collection)
        {
            return collection.GToFloor(t => t.ToList());
        }
        #endregion
    }

    /// <summary>
    /// 递归查询方向
    /// </summary>
    public enum RecursionDirection
    {
        /// <summary>
        /// 递归父族
        /// </summary>
        Parent,
        /// <summary>
        /// 递归子族
        /// </summary>
        Children,
        /// <summary>
        /// 递归全族(包括父族和子族)
        /// </summary>
        Family
    }
}
